/*
 * Išspaudzinimo į ekraną funkcijos
 */
#include "../../include/types.h"
#include "../../include/const.h"
#include "../../include/busIO.h"
#include "../../include/string.h"
#include "../../include/keyboard.h"
#include "../../include/task.h"
#include "../../include/console.h"
#include "../../include/ext2.h"
#include "../../include/process.h"
#include "../../include/monitor.h"

/* VGA vaizdo buferis prasideda ties 0xB8000 */
u16int *video_memory = (u16int *)0xB8000;
/* Dabartinės konsoles numeris */
u32int current;
/* Kintamieji laikantys kursorio kordinates */
u8int cursor_x;
u8int cursor_y;
/* Atributo baitas yra sudarytas iš dvejų nublu - žemesnio, kuris yra */
/* simbolių spalva ir aukštesnio, kuris yra fono spalva */
u8int simbolcolor = 0x0F;
u8int fontcolor;
u8int attributeByte = (0x00 /* Juoda */ << 4) | (15  & 0x0F /* Balta */);
/* Kursoriaus kordinatems pakeisti, "judinti" kursorių */
static void move_cursor(){
  u16int cursorLocation = cursor_y * 80 + cursor_x; /* Ekranas yra 80 simbolių pločio */
  
  outb(0x3D4, 14);                                  /* Pranešame VGA plokštei, kad dėsime kursoriaus aukštenyjį baitą */
  outb(0x3D5, cursorLocation >> 8);                 /* Siunčiame aukštnyjį kursoriaus baitą */
  outb(0x3D4, 15);                                  /* Pranešame VGA plokštei, kad dėsime kursoriaus žemesnyjį baitą */
  outb(0x3D5, cursorLocation);                      /* Siunčiame žemesnyjį kursoriaus baitą */
}                                
/* Pakelia teksta ekrane viena eilute aukščiau */
static void scroll(){
  int i = 0;
  /* Nusistatome tarpo simbolį su įprastomis spalvomis */
  u16int blank = 0x20 /* Tarpas */ | (attributeByte << 8);  
  
  /* 25 eilė yra paskutinė, vadinasi reikia pasislinkti į viršų */
  if(cursor_y >= 25){
    /* Perkeliame eilutes vaizdo buferyje taip kad aukščiausia būtų perrašyta */
    for(i = 0*80; i < 24*80; i++){
      video_memory[i] = video_memory[i+80];
    }
    /* Paskutinė eilutė dabar tuščia. Užpildome ją tarpais */
    for(i = 24*80; i < 25*80; i++){
      video_memory[i] = blank;
    }
    /* Pastatome kursorių į paskutinę eilutę */
    cursor_y = 24;
  }
}

void init_monitor(){
  current = 0;
  cursor_x = 0;
  cursor_y = 0;
  simbolcolor = 0x0F;
  fontcolor = 0;
}

/* X-[0;79] Y-[0;24] */
void gotoXY(u8int X, u8int Y){
  if((X >= 0)&&(X <= 79)&&(Y >= 0)&&(Y <= 24)){
    cursor_x = X;
    cursor_y = Y;
    move_cursor();
  }
  else{
    monitor_write("The X or Y value is too big or too small!");
  }
}
/* Pakeičia spalva ekrane simbolių */
void monitor_color(u8int attribute){
  simbolcolor = attribute;
  attributeByte = (fontcolor << 4) | (15  & simbolcolor);
}
void monitor_font(u8int attribute){
  fontcolor = attribute;
  attributeByte = (fontcolor << 4) | (15  & simbolcolor);
}
/* Atspauzdinti simbolį su klaviatūra */
void keyboard_monitor_put(char c, short shift){
  /* 8 aukštesnieji baitai yra tie, kuriuos reikia nusiusti į VGA plokštę */
  u16int attribute = attributeByte << 8;
  u16int *location;
  int i,j;

  /* Jei simboli yra backspace, grįžtame vienu simboliu atgal */
  if((c == 0x08)&&(cmd_find(current)->task == NULL)){
    if(cursor_x != 1){
      cursor_x--;
      location = video_memory + (cursor_y*80 + cursor_x);
      *location = ' ' | attribute;
    }
  }
  /* Create CONSOLE */
  else if((c == 0x0038)&&(shift == 1)){
    i = cmd_create();
    monitor_reload(i);
  }
  /* DELETE CONSOLE */
  else if((c == 0x001D)&&(shift == 1)){
    cmd_delete(current);
  }
  /* Kill Process */
  else if((c == 0x001B)&&(shift == 1)){
    cons_t *tmp = cmd_find(current);
    if(tmp->task != NULL){
      if(find_task(tmp->task->id)){
        kill(tmp->task->id);
      }
    }
  }
  else if((c == 0x004B)&&(shift==0)&&((cmd_find(current)->task == NULL)||(cmd_find(current)->readkey == TRUE))){
    if(cursor_x != 1){
      cursor_x--;
    }
  }
  else if((c == 0x004D)&&(shift==0)&&((cmd_find(current)->task == NULL)||(cmd_find(current)->readkey == TRUE))){
    if(cursor_x != 79){
      cursor_x++;
    }
  }
  /* Jei simbolis yra tab'as, tai padidiname kursoriaus X, bet per tiek kad dalitusi iš 8 */
  else if(c == '\t'){
    if((shift == 0)&&((cmd_find(current)->task == NULL)||(cmd_find(current)->readkey == TRUE))){
      cursor_x = (cursor_x+8) & ~(8-1);
    }
    else{
      monitor_reload_next();
    }
  }
  /* Jei simbolis yra carriage return */
  else if((c == '\r')&&((cmd_find(current)->task == NULL)||(cmd_find(current)->readkey == TRUE))){
    cursor_x = 0;
  }
  /* Jei simbolis yra nauja eilutė, gryžtame kursorium atgal ir pereiname į nauja eilutę */
  else if((c == '\n')&&(cmd_find(current)->task == NULL)){
    char* str = (char*) kmalloc(80*sizeof(char));
    char* str2 = (char*) kmalloc(80*sizeof(char));
    for(i=1; i<80;i++){
      str[i] = video_memory[cursor_y*80+i];
    }
    if((str[1] == 'c')&&(str[2] == 'l')&&(str[3] == 'e')&&(str[4] == 'a')&&(str[5] == 'r')&&(str[6] == ' ')){
      monitor_clear();
      location = video_memory + (cursor_y*80 + cursor_x);
      *location = '$' | (0x00 /* Juoda */ << 4) | (15  & DEFAULT_COLOR) << 8;
      cursor_x++;
    }
    else if((str[1] == 'l')&&(str[2] == 's')&&(str[3] == ' ')){
      ls_ext2(); 
      cursor_x = 0;
      cursor_y++;
      location = video_memory + (cursor_y*80 + cursor_x);
      *location = '$' | (0x00 /* Juoda */ << 4) | (15  & DEFAULT_COLOR) << 8;
      cursor_x++;      
    }
    else if((str[1] == 'c')&&(str[2] == 'd')&&(str[3] == ' ')&&(str[4] != ' ')){
      i=4;
      j=0;
      while((str[i] != ' ') ||(str[i++] != ' ')){
        str2[j] = str[i];
        i++;
        j++;
      }
      cd_ext2(str2);
      cursor_x = 0;
      cursor_y++;
      location = video_memory + (cursor_y*80 + cursor_x);
      *location = '$' | (0x00 /* Juoda */ << 4) | (15  & DEFAULT_COLOR) << 8;
      cursor_x++;
    }
    else if((str[1] == 't')&&(str[2] == 'e')&&(str[3] == 's')&&(str[4] == 't')&&(str[5] == '2')&&(str[6] == ' ')){
      monitor_write("\n");
      
      add_child(create_task(test2, "tst2", KERNEL_MODE, current));
      cursor_x = 1;
      cursor_y++;
    }
    else if((str[1] == 't')&&(str[2] == 'e')&&(str[3] == 's')&&(str[4] == 't')&&(str[5] == '1')&&(str[6] == ' ')){
      monitor_write("\n");
      
      add_child(create_task(test1, "tst1", KERNEL_MODE, current));
      cursor_x = 1;
      cursor_y++;
    }
		else if((str[1] == 'p')&&(str[2] == 'r')&&(str[3] == 'o')&&(str[4] == 'g')&&(str[5] == ' ')){
      monitor_write("\n");
      
			add_child(create_task(prog, "prog", KERNEL_MODE, current));
      cursor_x = 1;
      cursor_y++;
    }
		else if((str[1] == 'p')&&(str[2] == 'r')&&(str[3] == 'o')&&(str[4] == 'c')&&(str[5] == ' ')){
      monitor_write("\n");
      
			add_child(create_task(proc, "proc", KERNEL_MODE, current));
      cursor_x = 1;
      cursor_y++;
    }
    else if((str[1] == 'e')&&(str[2] == 'x')&&(str[3] == 't')&&(str[4] == '2')&&(str[5] == ' ')){
      ext2();
      cursor_x = 0;
      cursor_y++;
      location = video_memory + (cursor_y*80 + cursor_x);
      *location = '$' | (0x00 /* Juoda */ << 4) | (15  & DEFAULT_COLOR) << 8;
      cursor_x++;
    }
    else if(str[1] == ' '){
      cursor_x = 0;
      cursor_y++;
      location = video_memory + (cursor_y*80 + cursor_x);
      *location = '$' | (0x00 /* Juoda */ << 4) | (15  & DEFAULT_COLOR) << 8;
      cursor_x++;
    }
    else{
      monitor_write("\nNo such command");
      cursor_x = 0;
      cursor_y++;
      location = video_memory + (cursor_y*80 + cursor_x);
      *location = '$' | (0x00 /* Juoda */ << 4) | (15  & DEFAULT_COLOR) << 8;
      cursor_x++;
    }
  }
  /* Visi likusieji simboliai */
  else if((c >= ' ')&&((cmd_find(current)->task == NULL)||(cmd_find(current)->readkey == TRUE))){
    location = video_memory + (cursor_y*80 + cursor_x);
    *location = c | attribute;
    cursor_x++;
  }
  /* Patikriname ar nereikia pereiti į nauja eilutę */
  if(cursor_x >= 80){
    cursor_x = 0;
    cursor_y ++;
  }
  scroll();  /* Jei reikia sukuriame nauja eilutę */
  move_cursor(); /* Perkeliame kursorių */
}

void finish(task_t *task){
  if(task->console == current){
    cursor_x = 0;
    cursor_y++;
    video_memory[cursor_y*80 + cursor_x]= '$' | ((0x00 /* Juoda */ << 4) | (15  & DEFAULT_COLOR)) << 8;
    cursor_x++;
    cmd_find(current)->task = NULL;
    scroll();
    move_cursor();
  }
  else{
    cons_t *tmp = cmd_find(task->console);
    tmp->Y++;
    tmp->X=0;
    tmp->buffer[tmp->Y*80 + tmp->X] = '$' | (tmp->attribute << 8);
    tmp->X++;
    cmd_find(task->console)->task = NULL;
  }
}
/* Išspaudzina viena simbolį ekrane */
void monitor_put(char c){
  /* 8 aukštesnieji baitai yra tie, kuriuos reikia nusiusti į VGA plokštę */
  u16int attribute = attributeByte << 8;
  u16int *location;

  /* Jei simbolis yra tab'as, tai padidiname kursoriaus X, bet per tiek kad dalitusi iš 8 */
  if(c == 0x09){
    cursor_x = (cursor_x+8) & ~(8-1);
  }
  /* Jei simbolis yra carriage return */
  else if(c == '\r'){
    cursor_x = 1;
  }
  /* Jei simbolis yra nauja eilutė, gryžtame kursorium atgal ir pereiname į nauja eilutę */
  else if(c == '\n'){
    cursor_x = 0;
    cursor_y++;
  }
  /* Visi likusieji simboliai */
  else if(c >= ' '){
    location = video_memory + (cursor_y*80 + cursor_x);
    *location = c | attribute;
    cursor_x++;
  }
  /* Patikriname ar nereikia pereiti į nauja eilutę */
  if(cursor_x >= 80){
    cursor_x = 0;
    cursor_y ++;
  }
  scroll();  /* Jei reikia sukuriame nauja eilutę */
  move_cursor(); /* Perkeliame kursorių */
}
/* Išvalo ekraną */
void monitor_clear(){
  int i = 0;
  /* Nusistatome tarpo simbolį su įprastomis spalvomis */
  u16int blank = 0x20 /* Tarpas */ | (attributeByte << 8); 
  
  /* Užpildome vaizdo buferį tarpais */
  for(i = 0; i < 80*25; i++){
    video_memory[i] = blank;
  }
  cursor_x = 0;  /* Grąžiname kursorių atgal į pradžią */
  cursor_y = 0;  /* Grąžiname kursorių atgal į pradžią */
  move_cursor(); /* Perkeliame kursorių */
}

/* Išspaudzina ASCII simbolių eilutę ekrane */
void monitor_write(char *c){
  int i = 0;
  while(c[i]){
    monitor_put(c[i++]);
  }
}
/* Išspaudzina šešioliktainį skaičių ekrane */
void monitor_write_hex(u32int n){
  s32int tmp; 
  char noZeroes = 1;
  int i = 0;
  
  monitor_write("0x"); /* Atspauzdiname pradžią šešioliktainio skaičiaus */
  for(i = 28; i > 0; i -= 4){
    tmp = (n >> i) & 0xF;
    if(tmp == 0 && noZeroes != 0){
      continue;
    }
    if(tmp >= 0xA){
      noZeroes = 0;
      monitor_put (tmp-0xA+'a');
    }
    else{
      noZeroes = 0;
      monitor_put(tmp+'0');
    }
  }
  tmp = n & 0xF;
  if(tmp >= 0xA){
    monitor_put(tmp-0xA+'a');
  }
  else{
    monitor_put(tmp+'0');
  }
}
/* Išspaudzina dešimtainį skaičių ekrane */
void monitor_write_dec(u32int n){
  s32int acc = n;
  char c[32], c2[32];
  int i = 0, j = 0;
  
  if (n == 0){
    monitor_put('0');
    return;
  }
  while (acc > 0){
    c[i] = '0' + acc%10;
    acc /= 10;
    i++;
  }
  c[i] = 0;
  c2[i--] = 0;
  while(i >= 0){
    c2[i--] = c[j++];
  }
  monitor_write(c2);
}
void monitor_update(){
  monitor_clear();
  cons_t *tmp = cmd_find(current);
  int i;
  for(i = 0; i < 80*25; i++){
    video_memory[i] = tmp->buffer[i] | (attributeByte << 8);
  }
  move_cursor();
  if((tmp->task != NULL)&&(tmp->readkey==TRUE)){
    resume_task(tmp->task);
  }
}
void monitor_load(u32int nr){
  monitor_clear();
  int i;
  cons_t *tmp = cmd_find(nr);
  
  current = tmp->console_nr;
  cursor_x = tmp->X;
  cursor_y = tmp->Y;
  simbolcolor = tmp->char_color;
  fontcolor = tmp->font_color;
  attributeByte = tmp->attribute;
  for(i = 0; i < 80*25; i++){
    video_memory[i] = tmp->buffer[i] | (attributeByte << 8);
  }
  move_cursor();
  if((tmp->task != NULL)&&(tmp->readkey==TRUE)){
    resume_task(tmp->task);
  }
}

void monitor_reload_next(){
  u16int blank = 0x20 /* Tarpas */ | (attributeByte << 8); 
  int i;
  cons_t *tmp = cmd_find(current);
  /* Išsaugome dabartinės konsolės duomenis */

  for(i = 0; i < 80*25; i++){
    tmp->buffer[i] = blank;
  }
  for(i = 0; i < 80*25; i++){
    tmp->buffer[i] = video_memory[i] | (attributeByte << 8);
  }
  tmp->X = cursor_x;
  tmp->Y = cursor_y;
  tmp->char_color = simbolcolor;
  tmp->font_color = fontcolor;
  tmp->attribute= attributeByte;
  if((tmp->task != NULL)&&(tmp->readkey==TRUE)){
    suspend_task(tmp->task);
  }
  monitor_clear();
  
  tmp = cmd_find(tmp->next->console_nr);
  
  current = tmp->console_nr;
  cursor_x = tmp->X;
  cursor_y = tmp->Y;
  simbolcolor = tmp->char_color;
  fontcolor = tmp->font_color;
  attributeByte = tmp->attribute;
  for(i = 0; i < 80*25; i++){
    video_memory[i] = tmp->buffer[i] | (attributeByte << 8);
  }
  move_cursor();
  if((tmp->task != NULL)&&(tmp->readkey==TRUE)){
    resume_task(tmp->task);
  }
}

void monitor_reload(u32int nr){
  u16int blank = 0x20 /* Tarpas */ | (attributeByte << 8); 
  int i;
  cons_t *tmp = cmd_find(current);
  /* Išsaugome dabartinės konsolės duomenis */

  for(i = 0; i < 80*25; i++){
    tmp->buffer[i] = blank;
  }
  for(i = 0; i < 80*25; i++){
    tmp->buffer[i] = video_memory[i] | (attributeByte << 8);
  }
  tmp->X = cursor_x;
  tmp->Y = cursor_y;
  tmp->char_color = simbolcolor;
  tmp->font_color = fontcolor;
  tmp->attribute= attributeByte;
  if((tmp->task != NULL)&&(tmp->readkey==TRUE)){
    suspend_task(tmp->task);
  }
  monitor_clear();
  
  tmp = cmd_find(nr);
  
  current = tmp->console_nr;
  cursor_x = tmp->X;
  cursor_y = tmp->Y;
  simbolcolor = tmp->char_color;
  fontcolor = tmp->font_color;
  attributeByte = tmp->attribute;
  for(i = 0; i < 80*25; i++){
    video_memory[i] = tmp->buffer[i] | (attributeByte << 8);
  }
  move_cursor();
  if((tmp->task != NULL)&&(tmp->readkey==TRUE)){
    resume_task(tmp->task);
  }
}

u32int getcurrent(){
  return current;
}
void setcurrent(){
  current = 0;;
}
